#include "Protocol.hpp"
#include "Util.hpp"
// #include "Log.hpp"

// EndPoint——通信
// HttpRequest——请求
// 1. 读取请求行 和 请求报头
bool EndPoint::recvHttpRequestLine()
{
    auto& line = http_request.request_line; // 请求方法 url HTTP版本
    if(Util::readLine(sock_fd, line) > 0)   
    {
        line.resize(line.size()-1);
        //lg(INFO, "%s\n",line.c_str());
    }
    else
    {
        is_stop = true;
    }
    return false;
}
bool EndPoint::recvHttpRequestHeader()
{
    std::string line;
    while(true)
    {
        line.clear();
        if(Util::readLine(sock_fd, line) <= 0)
        {
            is_stop = true;
            break;
        }
        if(line == "\n")
        {
            http_request.blank = line;
            break;
        }
        line.resize(line.size()-1); // 将'\n'去掉
        http_request.request_header.push_back(line);
        //lg(INFO, "%s\n", line.c_str());
    }
    return is_stop;
}

// 2. 解析请求行 和 请求报头
void EndPoint::parseHttpRequestLine()
{
    // a.分割请求行信息
    auto& line = http_request.request_line;
    std::stringstream ss(line);
    ss >> http_request.line_method >> http_request.line_url >> http_request.line_http_version;
    // b. 请求信息格式化解析——同意转化为大写
    // b.1: 解析method
    auto& method = http_request.line_method;
    std::transform(method.begin(), method.end(), method.begin(), ::toupper);
    // b.2: 解析url
    auto& url = http_request.line_url;
    auto& path = http_request.url_path;
    auto& query_string = http_request.url_query_string;
    if(method == "GET")
    {
        // GET 方式请求，带参则执行cgi机制(url中含有 '?' )，不带参则为静态网页的请求
        size_t pos = url.find("?");
        if(pos != std::string::npos)
        {
            // 含有 ? -> 执行  cgi 机制
            Util::cutString(url, path, query_string, "?");
            http_request.cgi = true;
        }
        else{
            // 没有 ? -> 访问的是静态网页
            path = url;
        }
    }
    else if(method == "POST")
    {
        // 只要使用 POST 方式请求 HTTP服务器，就都要执行 cgi 机制
        path = url;
        http_request.cgi = true;
    }
    else
    {
        // 请求方式 非GET 非POST
        //lg(ERROR, "请求HTTP服务器方式 非GET 非POST.....");
    }

}
void EndPoint::parseHttpRequestHeader()
{
    std::string key;
    std::string value;
    for(auto& line: http_request.request_header)
    {
        if(Util::cutString(line, key, value, Heaser_Sep))
        {
            http_request.header_kv.insert({key, value});
        }
    }
}

// 3.读取请求正文
bool isNeedRecvHttpRequestBody(HttpRequest& http_request)
{
    auto& method = http_request.line_method;
    if(method == "POST")
    {
        auto& header_kv = http_request.header_kv;
        auto iter = header_kv.find("Content-Length");
        if(iter != header_kv.end())
        {
            //lg(INFO, "POST Method, Content-Length: %s\n", iter->second);
            http_request.header_content_length = std::stoi(iter->second);
            return true;
        }
    }
    return false;
}
bool EndPoint::recvHttpRequestBody()
{
    if(isNeedRecvHttpRequestBody(http_request))
    {
        int request_content_length = http_request.header_content_length;
        auto& body = http_request.request_body;
        int ch = 0;
        while(request_content_length)
        {
            ssize_t s = recv(sock_fd, &ch, 1, 0);
            if(s > 0)
            {
                body.push_back(ch);
                request_content_length--;
            }
            else
            {
                is_stop = true; // 读取失败
                break;
            }
        }
        if(!is_stop)
            //lg(INFO, "%s\n", body.c_str());
            std::cout << body << std::endl;
        else
            std::cout << "读取请求正文 失败...." << std::endl;
            //lg(ERROR, "读取请求正文 失败....");
    }
    return is_stop;
}

// HttpResponse——响应
// a. 执行cgi 机制的资源
int EndPoint::processCgi()  // GET带参式 or POST式
{
    std::string info = "打开 动态网页资源， 以 " + http_request.line_method + " 的方式打开....";
    //lg(INFO, "%s\n", info.c_str());
    std::cout << info << std::endl;
    
    // 1.前提变量声明
    int response_status_code = OK;
    auto& method = http_request.line_method;
    auto& request_url_path = http_request.url_path;
    auto& request_query_string = http_request.url_query_string; // GET带参的 参数字符串（"?"之后的字符串）
    auto& request_content_length = http_request.header_content_length;
    auto& request_body = http_request.request_body; //POST传参（向父进程的写端发送数据）
    auto& response_body = http_response.response_body;
    
    // 2.给子进程构建环境变量
    std::string child_method_env;
    std::string child_query_string_env;
    std::string child_content_length_env;

    // 3, 站在父进程的角度——两个匿名管道（一个用来读，另一个用来写）
    int input[2];   // 父进程读数据
    int output[2];  // 父进程写数据
    if(pipe(input) < 0){
        //lg(ERROR, "pip input error....");
        response_status_code = BAD_QUEST;
        return response_status_code;
    }
    if(pipe(output) < 0){
        //lg(ERROR, "pip output error....");
        response_status_code = BAD_QUEST;
        return response_status_code;
    }
    //lg(INFO, "create pipe (input, output) success...");

    // 4, 因为是新线程，但是从头到尾都只有一个进程，就是 httpserver
    //    所以需要 创建子进程，让子进程执行我们的 cgi 机制的功能
    pid_t pid = fork();
    if(pid == 0){   // child
        // c.1 子进程 关闭 input 的读端， output的写端（向 input 中写，ouput 中读）
        // std::cout << " 开始执行子进程...." << std::endl;
        close(input[0]);
        close(output[1]);
        // c.2 执行 CGI 机制：GET带参 or POST
        //     GET/POST 申请资源传递数据 的方式（通过环境变量传参）
        //     GET  --> query_string（环境变量）
        //     POST --> Content-Length（环境变量）
        // c.2.1 构建环境变量
        child_method_env = "METHOD=";
        child_method_env += method;
        putenv((char*)child_method_env.c_str());
        // c.2.2 通过判断请求方法来构建响应的环境变量
        if(method == "GET")
        {
            child_query_string_env = "QUERY_STRING=";
            child_query_string_env += request_query_string; // eg:QUERY_STRING=a=100&b=200
            putenv((char*)child_query_string_env.c_str());
            //lg(INFO, "GET Method Add Query_string Env");
        }
        else if(method == "POST")
        {
            child_content_length_env = "Content-Length=";
            child_content_length_env += std::to_string(request_content_length);
            putenv((char*)child_content_length_env.c_str());
            //lg(INFO, "POST Method Add Content-Length Env");
        }
        else{
            // TODO
        }
        // c.3 我们·在替换的程序内并不知道要往哪一个文件描述符中读写
        //     所以，就需要对文件描述符（input[1]写, output[0]读）进行重定向都总所周知的（1号文件描述符，0号文件描述符）
        dup2(input[1], 1);      // 通过1号文件描述符对 Input[1]进行写入
        dup2(output[0], 0);     // 通过0号文件描述符对 output[0]进行读取
        // c.4 程序替换系列 exec* 
        // std::cout << "request_url_path: " << request_url_path << std::endl;
        execl(request_url_path.c_str(), request_url_path.c_str(), nullptr);
        exit(1);
    }
    else if(pid < 0){  // error
        //lg(ERROR, "fork error....");
        return BAD_QUEST;
    }
    // parent
    // 关闭 input 的写端， output 的读端
    close(input[1]);
    close(output[0]);
    // p.1 要使用 POST 传正文方式传参，就需要父进程对它进行发送正文数据
    //     之后 “替换的子进程再来对它进行读取”
    if(method == "POST")
    {
        const char* start = request_body.c_str();
        int total = 0, size = 0;
        while ((total < request_content_length) && (size = write(output[1], request_body.c_str()+total, request_body.size()-total)) > 0)
        {
            total += size;
        }
    }
    // p.2 子进程向 input[0] 中发送其处理后的数据，父进程对这部分数据进行接受
    char ch = 0;
    while(read(input[0], &ch, 1))
    {   
        response_body.push_back(ch);
    }
    std::cout << "接受到的信息：" << response_body << std::endl;
    // p.3 父进程开始等待子进程

    int wait_status = 0;
    pid_t rid = waitpid(pid, &wait_status, 0);
    if(rid == pid)
    {
        // 等待子进程成功
        if(WIFEXITED(wait_status)){
            if(WEXITSTATUS(wait_status) == 0)
                response_status_code = OK;
            else
                response_status_code = BAD_QUEST;
        }
        else
            response_status_code = SERVER_ERROR;
    }
    std::cout << "子进程退出码: " <<  WEXITSTATUS(wait_status) << std::endl;
    // P.4 将剩下的 文件描述符关闭
    close(input[0]);
    close(output[1]);

    
    return response_status_code;
}

// b. 执行 静态网页 的资源
int EndPoint::processNonCgi()
{
    std::string info = "打开 静态网页资源， 以 " + http_request.line_method + " 的方式打开....";
    //lg(INFO, "%s\n", info.c_str());
    // 开始打开
    http_response.response_fd = open(http_request.url_path.c_str(), O_RDONLY);
    if(http_response.response_fd >= 0){ // 打开静态网页资源成功
        //lg(INFO, "open 静态网页成功......");
        std::cout << " open 静态网页成功...... " << std::endl;
        // std::cout << "请求行： " << http_request.request_line << std::endl;

        return OK;
    }
    return NOT_FOUND;
}

// c. 构建正常的响应报文
void EndPoint::buildOkResponse(std::string index_page)
{
    // 给用户返回响应正常的 index 页面
    std::string response_header_line = "Content-Type: ";
    response_header_line += suffixToDesc(http_request.url_suffix);
    response_header_line += LINE_END;
    http_response.response_header.push_back(response_header_line);
    response_header_line = "Content-Length: ";
    if(http_request.cgi){
        response_header_line += std::to_string(http_response.response_body.size());
    }
    else{
        response_header_line += std::to_string(http_request.size);
    }
    response_header_line += LINE_END;
    http_response.response_header.push_back(response_header_line);
}

// d. 构建异常的响应报文
void EndPoint::buildErrorResponse(std::string error_page)
{
    http_request.cgi = false;
    // 给用户返回对应的 404 页面
    http_response.response_fd = open(error_page.c_str(), O_RDONLY);
    if(http_response.response_fd > 0)
    {
        // struct stat是 Unix/Linux 系统中用于描述文件元数据的结构体，定义在 <sys/stat.h>头文件中。
        // 存储了文件类型、权限、大小、时间戳等信息，通常用于 stat()、fstat()、lstat()等系统调用。
        // stat(文件名，struct stat*): 获取文件信息
        struct stat st;
        stat(error_page.c_str(), &st);
        http_request.size = st.st_size;

        std::string response_header_line = "Content-Type: text/html";
        response_header_line += LINE_END;
        http_response.response_header.push_back(response_header_line);
        response_header_line = "Content-Length: ";
        response_header_line += std::to_string(st.st_size);
        response_header_line += LINE_END;
        http_response.response_header.push_back(response_header_line);
    }
}

// e. 构建响应——响应填充
void EndPoint::buildHttpResponseHelper()
{
    // 1,获取状态码
    auto& code = http_response.status_line_code;
    // 2，构建状态行
    auto& line = http_response.response_status_line;
    line += HTTP_VERSION;
    line +=  " " + std::to_string(code) + " " + codeToDesc(code) + LINE_END;
    // 3, 构建响应正文
    std::string response_url_path = WEBWOOT;
    response_url_path += "/";
    switch (code)
    {
    case OK:
        std::cout << "成功的构建相应：http_response.body: " << http_response.response_body << std::endl;
        response_url_path += HOME_PAGE;
        buildOkResponse(response_url_path);
        break;
    case NOT_FOUND:
        response_url_path += PAGE_404;
        buildErrorResponse(response_url_path);
    default:
        break;
    }
}


//  EndPoint 开始通讯处理
// 1. http服务器是否运行
bool EndPoint::isStop()
{
    return is_stop;
}

// 2. 识别并处理请求
void EndPoint::recvHttpRequest()
{
    if(!recvHttpRequestLine() && !recvHttpRequestHeader())
    {
        // 开始解析——解析好的字段填充到 HttpRequest 类中
        parseHttpRequestLine();
        parseHttpRequestHeader();
        recvHttpRequestBody();
        // std::cout << " 识别并处理请求 成功" << std::endl;
        // std::cout << "请求行： " << http_request.request_line << std::endl;
    }
}

// 3. 构建响应
void EndPoint::buildHttpResponse()
{
    // 因为 goto 之后的代码不能 再次定义变量，所以 url_path , struct stat, f_sep定义到这里
    std::string url_complete_path;  // 完整url路径
    struct stat st;
    size_t f_sep;   // 用来获取后缀的标识，后缀的下标位置

    auto& response_code = http_response.status_line_code;
    // 1. Method 是 GET or POST
    auto& method = http_request.line_method;
    if(method != "POST" && method != "GET")
    {
        // 这就是非法的请求方式
        //lg(WARN, "method is not right....");
        // std::cout << "method is not right...." << std::endl;
        response_code = BAD_QUEST;
        goto END;
    }
    // 2，请求方法不是 GET 就是 POST, 接下来构建 完整的网页路径资源
    url_complete_path = http_request.url_path;
    http_request.url_path = WEBWOOT;
    http_request.url_path += url_complete_path;
    url_complete_path = http_request.url_path;
    if(url_complete_path[url_complete_path.size()-1] == '/')
    {
        // wwwroot/    or    wwwroot/a/b/c
        http_request.url_path += HOME_PAGE;
    }
    // 现在已经构建完毕了，eg: wwwroot/a/b/c/index.html
    // 3，判断网页资源是否存在
    if(stat(http_request.url_path.c_str(), &st) == 0)   // 请求的网页资源存在
    {
        if(S_ISDIR(st.st_mode)){    // 请求的资源是一个目录，需要做特殊处理
            http_request.url_path += '/';
            http_request.url_path += HOME_PAGE;
            stat(http_request.url_path.c_str(), &st);
        }
        if((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)){
            // 判断访问的文件是否是可执行文件，注意：目录本身就是有可执行的权限的
            // 需要特殊处理——使用 cgi 机制
            http_request.cgi = true;
        }
        http_request.size = st.st_size;
    }
    else    // 请求的网页资源不存在
    {
        std::string info = http_request.url_path;
        info += " Not Found...";
        //lg(WARN, "%s\n", info.c_str());
        std::cout << info << std::endl;
        response_code = NOT_FOUND;
        goto END;
    }
    // 4, 此时网页资源是一定存在的
    f_sep = http_request.url_path.find(".");
    if(f_sep != std::string::npos){
        // 找到后缀的位置了
        http_request.url_suffix = http_request.url_path.substr(f_sep);
    }
    else{
        // 没有找到后缀，默认就是.html
        http_request.url_suffix = ".html";
    }
    // 5, 可以开始执行网络资源了
    if(http_request.cgi){   // 执行 cgi 机制的资源(动态网页的资源)
        response_code = processCgi();
    }
    else{   // 执行静态网页的资源
        response_code = processNonCgi();
    }

END:
    // 开始一些列响应的填充
    buildHttpResponseHelper();
    return;
}

// 4. 发送响应
void EndPoint::sendHttpResponse()
{
    // 发送响应行
    send(sock_fd, http_response.response_status_line.c_str(), http_response.response_status_line.size(), 0);
    // 发送响应报头
    for(auto& iter : http_response.response_header)
    {
        send(sock_fd, iter.c_str(), iter.size(), 0);
    }
    // 发送空行
    send(sock_fd, http_response.response_blank.c_str(), http_response.response_blank.size(), 0);
    // 发送正文（网页资源）
    if(http_request.cgi)
    {
        auto& response_body = http_response.response_body;
        int total = 0, size = 0;
        while((total < http_response.response_body.size()) && (size = send(sock_fd, response_body.c_str()+total, response_body.size()-total, 0)) > 0)
        {
            total += size;
        }
    }
    else
    {
        sendfile(sock_fd, http_response.response_fd, nullptr, http_request.size);
        close(http_response.response_fd);
    }
}